/*
 *  Copyright 2019-2020 Zheng Jie
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.anjiplus.template.gaea.generator.modules.generator.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ZipUtil;
import com.anji.plus.gaea.bean.KeyValue;
import com.anji.plus.gaea.curd.entity.GaeaBaseEntity;
import com.anjiplus.template.gaea.generator.config.GaeaGeneratorProperties;
import com.anjiplus.template.gaea.generator.exception.BadRequestException;
import com.anjiplus.template.gaea.generator.modules.column.dao.GeneratorColumn;
import com.anjiplus.template.gaea.generator.modules.column.dao.GeneratorColumnRepository;
import com.anjiplus.template.gaea.generator.modules.generator.controller.param.GeneratorParam;
import com.anjiplus.template.gaea.generator.modules.table.dao.GeneratorTable;
import com.anjiplus.template.gaea.generator.modules.table.dao.TableInfo;
import com.anjiplus.template.gaea.generator.utils.FileUtil;
import com.anjiplus.template.gaea.generator.utils.PageUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.SingleColumnRowMapper;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author Zheng Jie
 * @date 2019-01-02
 */
@Service
public class GeneratorServiceImpl implements GeneratorService {
    private static final Logger log = LoggerFactory.getLogger(GeneratorServiceImpl.class);

    @Autowired
    JdbcTemplate baseJdbcTemplate;

    @Autowired
    private GeneratorColumnRepository generatorColumnRepository;

    @Autowired
    private GeneratorUtilService genUtil;

    @Autowired
    private GaeaGeneratorProperties gaeaGeneratorProperties;

    @Override
    public List<KeyValue> getSchemas() {
        String schemas = gaeaGeneratorProperties.getSchemas();
        // 使用预编译防止sql注入
        String sql = "select SCHEMA_NAME from information_schema.schemata";
        if(StringUtils.isNotEmpty(schemas)) {
            sql = sql.concat(" where schema_name in (" + schemas + ")");
        }
        List<String> result = baseJdbcTemplate.query(sql, new SingleColumnRowMapper<>());

        return result.stream().map(item->new KeyValue(item,item)).collect(Collectors.toList());
    }

    private boolean isDefaultSchema(String schema){
        return StringUtils.isEmpty(schema) || schema.equals("-");
    }

    @Override
    public Object getTables(String schema) {
        if(isDefaultSchema(schema)){
            schema = "(select database())";
        }else{
            schema = "'"+schema+"'";
        }

        // 使用预编译防止sql注入
        String sql = "select table_schema,table_name,create_time, engine, "
                + "table_collation, table_comment from information_schema.tables " +
                " where table_schema = " + schema +
                " order by create_time desc";
        List<Map<String,Object>> result = baseJdbcTemplate.queryForList(sql);
        List<TableInfo> tableInfos = new ArrayList<>();
        for (Map<String,Object> obj : result) {
            tableInfos.add(new TableInfo(obj.get("table_schema"),obj.get("table_name"),obj.get("create_time"),
                    obj.get("engine"),obj.get("table_collation"),
                    ObjectUtil.isNotEmpty(obj.get("table_comment")) ?obj.get("table_comment") : "-"));
        }
        return tableInfos;
    }

    @Override
    public Page getTables(GeneratorParam generatorParam) {
        // 查询参数
        String schema = generatorParam.getSchema();
        String tableName = generatorParam.getTableName();
        if(isDefaultSchema(schema)){
            schema = "(select database())";
        }else{
            schema = "'"+schema+"'";
        }
        String excludeTablesSql = "";
        if(StringUtils.isNotBlank(gaeaGeneratorProperties.getExcludeTableSql())){
            excludeTablesSql = gaeaGeneratorProperties.getExcludeTableSql();
        }
        // 使用预编译防止sql注入
        String tableQry = StringUtils.isNotBlank(tableName) ? ("'%" + tableName + "%' ") : "'%%' ";

        // 总共多少条
        String countSql = "SELECT COUNT(*) from information_schema.tables where table_schema = "+schema+" and table_name like "+tableQry+excludeTablesSql;
        Integer totalCount = baseJdbcTemplate.queryForObject(countSql,Integer.class);

        int[] startEnd = PageUtil.getLimit(generatorParam, totalCount);
        String sql = "select table_schema,table_name ,create_time , engine, "
                + " table_collation, table_comment from information_schema.tables " +
                " where table_schema = "  + schema +
                " and table_name like " + tableQry + excludeTablesSql+" order by create_time desc";
        sql = sql.concat(" limit "+startEnd[0]+","+startEnd[1]);
        List<Map<String,Object>> result = baseJdbcTemplate.queryForList(sql);
        List<TableInfo> tableInfoList = new ArrayList<>();
        for (Map<String,Object> obj : result) {
            tableInfoList.add(new TableInfo(obj.get("table_schema"),obj.get("table_name"),obj.get("create_time"),
                    obj.get("engine"),obj.get("table_collation"),
                    ObjectUtil.isNotEmpty(obj.get("table_comment")) ?obj.get("table_comment") : "-"));
        }

        return PageUtil.toPage(tableInfoList, totalCount, generatorParam);
    }

    @Override
    public List<GeneratorColumn> getColumns(String schema, String tableName) {
        LambdaQueryWrapper<GeneratorColumn> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(GeneratorColumn::getSchemaName,schema).eq(GeneratorColumn::getTableName,tableName)
                .orderByAsc(GaeaBaseEntity::getId);
        List<GeneratorColumn> list = generatorColumnRepository.selectList(wrapper);
        if (CollectionUtil.isNotEmpty(list)) {
            return list;
        } else {
            list = queryColumns(schema,tableName);
            // columnInfos.stream().forEach(item->columnInfoRepository.updateById(item));
            return list;
        }
    }

    @Override
    public List<GeneratorColumn> queryColumns(String schema, String tableName) {
        if(isDefaultSchema(schema)){
            schema = "(select database())";
        }else{
            schema = "'"+schema+"'";
        }
        String sql = "select table_schema,column_name, is_nullable, data_type, "
                + "column_comment, column_key, extra,character_maximum_length as maxLength," +
                " column_default as defaultValue" +
                " from information_schema.columns " +
                " where table_name = ? and table_schema = " + schema +" order by ordinal_position";
        List<Map<String,Object>> result = baseJdbcTemplate.queryForList(sql,tableName);
        List<GeneratorColumn> generatorColumnInfos = new ArrayList<>();
        GeneratorColumn c = null;
        for (Map<String,Object> arr : result) {
            c = new GeneratorColumn(arr.get("table_schema")+"",tableName,
                    arr.get("column_name").toString(),"NO".equals(arr.get("is_nullable")),
                    arr.get("data_type").toString(),
                    ObjectUtil.isNotNull(arr.get("column_comment")) ? arr.get("column_comment").toString(): null,
                    ObjectUtil.isNotNull(arr.get("column_key")) ? arr.get("column_key").toString() : null,
                    ObjectUtil.isNotNull(arr.get("extra")) ? arr.get("extra").toString() : null);
            if(arr.get("maxLength")!=null) {
                c.setMaxLength("" + arr.get("maxLength"));
            }
            if(arr.get("defaultValue")!=null && "String,Integer".indexOf(c.getColumnType())>=0) {
                c.setDefaultValue(arr.get("defaultValue"));
            }
            generatorColumnInfos.add(c);
        }
        return generatorColumnInfos;
    }

    @Override
    public void sync(List<GeneratorColumn> generatorColumnInfos, List<GeneratorColumn> generatorColumnInfoList) {
        // 第一种情况，数据库类字段改变或者新增字段
        for (GeneratorColumn col : generatorColumnInfoList) {
            // 根据字段名称查找
            List<GeneratorColumn> generatorColumns = generatorColumnInfos.stream()
                    .filter(c -> c.getColumnName().equals(col.getColumnName()))
                    .collect(Collectors.toList());
            // 如果能找到，就修改部分可能被改字段
            if (CollectionUtil.isNotEmpty(generatorColumns)) {
                GeneratorColumn generatorColumn = generatorColumns.get(0);
                generatorColumn.setColumnType(col.getColumnType());
                generatorColumn.setExtra(col.getExtra());
                generatorColumn.setKeyType(col.getKeyType());
                if (StringUtils.isBlank(generatorColumn.getRemark())) {
                    generatorColumn.setRemark(col.getRemark());
                }
                generatorColumnRepository.updateById(generatorColumn);
            } else {
                // 如果找不到，则保存新字段信息
                generatorColumnRepository.insert(col);
            }
        }
        // 第二种情况，数据库字段删除了
        for (GeneratorColumn col : generatorColumnInfos) {
            // 根据字段名称查找
            List<GeneratorColumn> generatorColumns = generatorColumnInfoList.stream()
                    .filter(c -> c.getColumnName().equals(col.getColumnName()))
                    .collect(Collectors.toList());
            // 如果找不到，就代表字段被删除了，则需要删除该字段
            if (CollectionUtil.isEmpty(generatorColumns)) {
                generatorColumnRepository.deleteBatchIds(generatorColumns.stream()
                        .map(i->i.getId()).collect(Collectors.toList()));
            }
        }
    }

    @Override
    public void saveColumnGenConfig(List<GeneratorColumn> cols) {
        if(cols.get(0).getId()==null){
            generatorColumnRepository.insertBatch(cols);
            return;
        }
        cols.stream().forEach(col->{
            generatorColumnRepository.updateById(col);
        });
    }

    @Override
    public void generator(GeneratorTable generatorTable, List<GeneratorColumn> generatorColumns) {
        if (generatorTable.getId() == null) {
            throw new BadRequestException("请先配置生成器");
        }
        try {
            genUtil.generatorCode(generatorColumns, generatorTable);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new BadRequestException("生成失败，请手动处理已生成的文件");
        }
    }

    @Override
    public List<Map<String, Object>> preview(GeneratorTable generatorTable, List<GeneratorColumn> generatorColumns) {
        if (generatorTable.getId() == null) {
            throw new BadRequestException("请先配置生成器");
        }
        List<Map<String, Object>> genList = genUtil.preview(generatorColumns, generatorTable);
        return genList;
    }

    @Override
    public void download(GeneratorTable generatorTable, List<GeneratorColumn> generatorColumns,
                         HttpServletRequest request, HttpServletResponse response) {
        if (generatorTable.getId() == null) {
            throw new BadRequestException("请先配置生成器");
        }
        try {
            File file = new File(genUtil.download(generatorColumns, generatorTable));
            String zipPath = file.getPath() + ".zip";
            ZipUtil.zip(file.getPath(), zipPath);
            FileUtil.downloadFile(request, response, new File(zipPath), true);
        } catch (Exception e) {
            log.error("download-error,{}", generatorTable,e);
            throw new BadRequestException("打包失败");
        }
    }
}
